
%{
#include <ast.h>
int yyerror(char *s);
int yylex(void);

extern Node* global_ast_node;
%}

%union{

    struct
    {
        Node*   node;
        struct
        {
            string  *text;
            int     line;
        } lexVal;
    };
}

%start	input

%token  <lexVal>    IDENTIFIER
%token  <lexVal>    CONST
%token  <lexVal>    '!'
%token  <lexVal>    '+'
%token  <lexVal>    '-'
%token  <lexVal>    '*'
%token  <lexVal>    '/'
%token  <lexVal>    '%'
%token  <lexVal>    '<'
%token  <lexVal>    '>'
%token  <lexVal>    OP_LE
%token  <lexVal>    OP_GE
%token  <lexVal>    OP_EQ
%token  <lexVal>    OP_NE
%token  <lexVal>    OP_AND
%token  <lexVal>    OP_OR
%token  <lexVal>    IF
%token  <lexVal>    ELSE
%token  <lexVal>    DO
%token  <lexVal>    WHILE
%token  <lexVal>    FOR



%type   <node>  variable_declaration;
%type   <node>  identifier;
%type   <node>  basic_identifier;
%type   <node>  bool_expression;
%type   <node>  expression;
%type   <node>  array_accessor;
%type   <node>  statement;
%type   <node>  statement_block;
%type   <node>  block;

%%

input
    :/* empty */
    | block { global_ast_node =  $1 }
		;

variable_declaration
    : identifier identifier ';'	{ $$ = new VariableDeclaration ($1, $2); }
    ;

statement
    : expression ';'				{ $$ = new Statement ($1); }
    | IF '(' expression ')' block               { $$ = new IfThenElse ($3, $5, 0); }
    | IF '(' expression ')' block ELSE block    { $$ = new IfThenElse ($3, $5, $7); }
    ;

statement_block
    :                                           { $$ = new StatementBlock (); }
    | statement_block statement			{ ((StatementBlock*)($1))->Add ($2); $$ = $1; }
    ;




block
    : statement					{ $$ = $1; }
    | '{' statement_block '}'                   { $$ = $2; }
    ;


bool_expression
    : identifier                                { $$ = $1; }
    | expression '<' expression                 { $$ = new BinOp ($1, *$2.text, $2.line, $3); }
    | expression '>' expression                 { $$ = new BinOp ($1, *$2.text, $2.line, $3); }
    | expression OP_LE expression               { $$ = new BinOp ($1, *$2.text, $2.line, $3); }
    | expression OP_GE expression               { $$ = new BinOp ($1, *$2.text, $2.line, $3); }
    | expression OP_EQ expression               { $$ = new BinOp ($1, *$2.text, $2.line, $3); }
    | expression OP_NE expression               { $$ = new BinOp ($1, *$2.text, $2.line, $3); }
    | expression OP_AND expression              { $$ = new BinOp ($1, *$2.text, $2.line, $3); }
    | expression OP_OR expression               { $$ = new BinOp ($1, *$2.text, $2.line, $3); }
    | '!' expression                            { $$ = new UnOp (*$1.text, $1.line, $2); }
    | '(' expression ')'                        { $$ = new ParenExp ($2); }
    ;

expression
    : bool_expression                               { $$ = $1; }
    | expression '+' expression                     { $$ = new BinOp ($1, *$2.text, $2.line, $3); }
    | expression '-' expression                     { $$ = new BinOp ($1, *$2.text, $2.line, $3); }
    | expression '*' expression                     { $$ = new BinOp ($1, *$2.text, $2.line, $3); }
    | expression '/' expression                     { $$ = new BinOp ($1, *$2.text, $2.line, $3); }
    | expression '%' expression                     { $$ = new BinOp ($1, *$2.text, $2.line, $3); }
    | bool_expression '?' expression ':' expression { $$ = new TerOp ($1, $3, $5); }
    ;


identifier
    : basic_identifier                  { $$ = $1 }
    | '(' identifier ')'                { $$ = new ParenIdentifier ($2); }
    | identifier array_accessor         { $$ = new ArrayIdentifier ($1, $2); }
    | identifier '.' identifier         { $$ = new DotIdentifier ($1, $3); }
    ;

basic_identifier
    : CONST                             { $$ = new Identifier (*$1.text, $1.line); }
    | IDENTIFIER                        { $$ = new Identifier (*$1.text, $1.line); }
    ;

array_accessor
    :                                   { $$ = 0; }
    | '[' expression ']' array_accessor { $$ = new ArrayAccessor ($2, $4) ;}
    ;
%%

int yyerror(string s)
{
    extern int yylineno;	// defined and maintained in lex.c
    extern char *yytext;	// defined and maintained in lex.c

    cerr << "ERROR: " << s << " at symbol \"" << yytext;
    cerr << "\" on line " << yylineno << endl;
    exit(1);
}

int yyerror(char *s)
{
    return yyerror(string(s));
}

/* vim:set ts=2 sw=2 noet */
